
/* GCSx
** ENTITY.H
**
** Entity support (instance of a script) (no edit-specific version)
** Includes bytecode interpreter
*/

/*****************************************************************************
** Copyright (C) 2003-2006 Janson
**
** This program is free software; you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation; either version 2 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program; if not, write to the Free Software
** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
*****************************************************************************/

#ifndef __GCSx_ENTITY_H_
#define __GCSx_ENTITY_H_

// Intentionally kept as simple and fast as possible-
//  no virtual table, some inline property sets
class Entity {
public:
    enum {
        DEFAULT_PRIORITY = 100
    };
    
    // Scripting-accessible properties
    enum {
        // Name of entity (not required/not unique/LOWERCASE)
        PROP_NAME = 0,

        // Linked to a sprite
        PROP_SPRITE,

        // User-assigned priority- lower runs first (equal-
        // oldest ie lowest ID runs first)
        PROP_PRIORITY,

        PROP_LAST
    };
    RunData varTable[PROP_LAST];
    
protected:
    // World pointer not included, but will add if needed

    // Numeric ID- computer-generated- unique to world- nonzero
    int id;

    // As long as positive, object must NOT delete itself
    // unless game is unloading. (variable references)
    int ref;
    
    // Active entities lock resources, etc.
    int active;
    int scriptLocked;
    void activateResource();
    void deactivateResource();
    
    friend struct ltEntity;
    
    // Script, Ptr not owned by us
    class Script* script;
    
    // Runtime details
    // Status- uninit = never ran, stopped = has run (init completed) but stopped
    // init/active/idle = can't unlock script as it's in use (init means in init stage)
    enum {
        RUN_UNINIT = 0,
        RUN_STOPPED,
        RUN_INIT,
        RUN_ACTIVE,
        RUN_IDLE,
    };
    int runStatus;
    int scriptType;
    Stack stack;
    // i = instruction pointer; NULL = stopped
    Uint32* i;
    // Position of first non-local on stack
    Uint32 endOfLocals;
    
    // Wipe entire stack, including locals
    void wipeStack();
    
public:
    // Id must be unique; use ID of 0 if we're going to load/create anyways
    // Can also use ID of 0 for internal temporaries, etc.
    Entity(int myId = 0);
    ~Entity();

    // Active entities use resources; inactive do not (similar to locking
    // but no count is incremented- this is on or off only) Sprite is
    // specifically not affected by us, assumed handled elsewhere
    // Inactive resources that were mid-execution, however, retain locks
    void setActive();
    void setInactive();
    
    // Sets
    void setName(const std::string& newName) {
        *((std::string*)varTable[PROP_NAME].p) = newName;
        toLower(*((std::string*)varTable[PROP_NAME].p));
    }
    void setSprite(class Sprite* newSprite);
    // (starts new script at beginning as soon as active)
    // Even if same script, still restarts script
    void setScript(class Script* newScript);
    
    // Accessors
    int getId() const { return id; }
    int getScriptType() const { return scriptType; }
    const std::string* getName() const { return (std::string*)varTable[PROP_NAME].p; }
    class Script* getScript() const { return script; }
    
#ifdef INTERPRETASSERT
    // Only used when debugging is enabled
    Uint32 getEndOfLocals() const { return endOfLocals; }
#endif
    
    // Run one "cycle" IF active
    // 100% UNSAFE for threading- uses global/static vars for speed
    void cycle();

    // Only interpreter functions that access protected fields go here
    static void op_init();
    static void op_stop();
    static void op_ret();
    static void op_retvoid();
    static void op_debug();
    static void op_idle();
    static void seRefEntity(StackEntry* s) {
        interpretAssert(s->type == STACK_ENTITY);
        if (s->data.p) ++(((Entity*)s->data.p)->ref);
    }
    static void seDerefEntity(StackEntry* s) {
        interpretAssert(s->type == STACK_ENTITY);
        if (s->data.p) --(((Entity*)s->data.p)->ref);
    }
};

// For ordering entities in priority queue
// @TODO: order on scripttype also?
// @TODO: ensure reordering done on changing priority/scripttype
struct ltEntity {
    bool operator() (const Entity* s1, const Entity* s2) const {
        return (s1->varTable[Entity::PROP_PRIORITY].i < s2->varTable[Entity::PROP_PRIORITY].i) ||
                ((s1->varTable[Entity::PROP_PRIORITY].i == s2->varTable[Entity::PROP_PRIORITY].i) &&
                 (s1->id < s2->id));
    }
};

// Multimap for names- 1) names are not unique 2) sorted iteration is necessary
typedef std::multimap<const char*, Entity*, map_ltstr> EntityMap;
// Hash for IDs- 1) as fast as possible 2) sorted iteration handled separately
typedef hash_map<int, Entity*, hash<int> > EntityIndex;
// List for sorted iteration- sorted by user priority, then ID
typedef std::set<Entity*, ltEntity> EntityOrder;

// Call at program init
void fillInterpreterTables();

// Call FIRST when shutting down a game (clears references, etc.)
void interpreterCleanup();

#endif

